package coreMiddleware

import (
	"context"
	"log"
	"runtime/debug"

	"gitee.com/xgh2012/api-core/coreBiz"
	"github.com/gin-gonic/gin"
	"github.com/google/uuid"
	"github.com/spf13/cast"
)

const (
	CtxKeyTraceId    = "trace_id"
	CtxKeyActionName = "action_name"
	CtxKeyUid        = "uid"
	CtxKeyMobile     = "mobile"
	CtxKeyIdCard     = "id_card"
)

// TraceMiddleware 注入 trace-id 到上下文及返回响应头
func TraceMiddleware() gin.HandlerFunc {
	return func(c *gin.Context) {
		traceId := c.GetHeader("X-Trace-Id")
		if traceId == "" {
			traceId = uuid.New().String()
		}
		ctx := context.WithValue(c.Request.Context(), CtxKeyTraceId, traceId)
		c.Request = c.Request.WithContext(ctx)

		c.Header("X-Trace-Id", traceId)

		// 捕获 panic
		defer func() {
			if err := recover(); err != nil {
				// 打印详细堆栈
				stack := debug.Stack()
				log.Printf("[panic] 处理请求错误 \n错误信息:%+v \n堆栈:%+v\n", err, string(stack))
			}
		}()

		c.Next()
	}
}

// GetTraceId 从 context.Context 中获取 trace-id
func GetTraceId(ctx context.Context) string {
	if v := ctx.Value(CtxKeyTraceId); v != nil {
		if traceId, ok := v.(string); ok {
			return traceId
		}
	}
	return ""
}

// SetActionName 从 context.Context 中获取 访问接口的名称
func SetActionName(ctx context.Context, actionName string) context.Context {
	return context.WithValue(ctx, CtxKeyActionName, actionName)
}

// GetActionName 从 context.Context 中获取 访问接口的名称
func GetActionName(ctx context.Context) string {
	if v := ctx.Value(CtxKeyActionName); v != nil {
		if actionName, ok := v.(string); ok {
			return actionName
		}
	}
	return ""
}

// SetUserInfo 从 context.Context 中设置用户信息
func SetUserInfo(ctx context.Context, uid int64, mobile, idCard string) context.Context {
	if uid > 0 {
		ctx = context.WithValue(ctx, CtxKeyUid, cast.ToString(uid))
	}
	if !coreBiz.IsEmpty(mobile) {
		ctx = context.WithValue(ctx, CtxKeyMobile, mobile)
	}
	if !coreBiz.IsEmpty(idCard) {
		ctx = context.WithValue(ctx, CtxKeyIdCard, idCard)
	}

	return ctx
}

type CtxUserInfo struct {
	Uid    string
	Mobile string
	IdCard string
}

// GetUserInfo 获取用户信息
func GetUserInfo(ctx context.Context) CtxUserInfo {
	res := CtxUserInfo{}
	if v := ctx.Value(CtxKeyUid); v != nil {
		if uid, ok := v.(string); ok {
			res.Uid = uid
		}
	}
	if v := ctx.Value(CtxKeyMobile); v != nil {
		if mobile, ok := v.(string); ok {
			res.Mobile = mobile
		}
	}
	if v := ctx.Value(CtxKeyIdCard); v != nil {
		if idCard, ok := v.(string); ok {
			res.IdCard = idCard
		}
	}
	return res
}

// CreateNewRequestContext 创建新的请求上下文
func CreateNewRequestContext(c *gin.Context) context.Context {
	ctx := context.Background()
	if traceId := GetTraceId(c.Request.Context()); traceId != "" {
		ctx = context.WithValue(ctx, CtxKeyTraceId, traceId)
	}
	if actionName := GetActionName(c.Request.Context()); actionName != "" {
		ctx = context.WithValue(ctx, CtxKeyActionName, actionName)
	}
	userInfo := GetUserInfo(c.Request.Context())
	if !coreBiz.IsEmpty(userInfo.Uid) {
		ctx = context.WithValue(ctx, CtxKeyUid, userInfo.Uid)
	}
	if !coreBiz.IsEmpty(userInfo.Mobile) {
		ctx = context.WithValue(ctx, CtxKeyMobile, userInfo.Mobile)
	}
	if !coreBiz.IsEmpty(userInfo.IdCard) {
		ctx = context.WithValue(ctx, CtxKeyIdCard, userInfo.IdCard)
	}
	return ctx
}
